/*
 * LessonMapper 2.Copyright (C) Olivier Motelet.This program is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public Licenseas published by the Free Software Foundation; either version 2of the License, or (at your option) any later version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package lessonMapper.query;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;

import lessonMapper.diffusion.Diffusion;
import lessonMapper.lom.LOM;
import lessonMapper.lom.LOMAttribute;
import lessonMapper.lom.LOMRestrictionSet;
import lessonMapper.lom.LOMRestrictionValue;
import lessonMapper.lom.LOMValue;
import lessonMapper.lom.LOMValueInt;
import lessonMapper.lom.RestrictionOperator;

import org.tartarus.snowball.SnowballProgram;

import util.Utils;

/**
 * LOMQuery is respondible for generating semantically rich queries for
 * retrieving LOM element it is also responsible for ranking the results of this
 * query. Basically, the query is generated based on three main elements: - a
 * keyword query = each keyword must be find in at least one attribute of the
 * resulted LOM - values given to the attributes of the associated LOM = the
 * resulting LOM should match with these values - the restriction values
 * generated by the diffusion framework = the resulting LOM should match with
 * the value range given by the restrictions
 * 
 * Result ranking is based on the suggestion values generated by the diffusion
 * framework. A result obtained a additional point when a attribute value match
 * with one of the suggestion. Better rank is given to results accumulating more
 * points.
 * 
 * @author omotelet
 */

public class LOMQueryOLD {

	/**
	 * 
	 */
	public static final String ITSQueryAttributes = "resources/queryAttributes.xml";

	/**
	 * 
	 */
	public static Vector<LOMAttribute> ITSAttributes = LOMAttribute
			.getAttributeList(LOMQueryOLD.class.getResource(ITSQueryAttributes));

	/**
	 * 
	 */
	protected LOM itsLOM;

	/**
	 * 
	 */
	protected String itsKeywordQuery;

	/**
	 * 
	 */
	protected boolean isFuzzyKeywordQuery = false;

	/**
	 * 
	 */
	protected boolean isFuzzyValueBasedCondition = false;

	/**
	 * 
	 */
	protected boolean considerValueBasedConditions = false;

	/**
	 * 
	 * 
	 * @param aLOM 
	 * @param aFuzzyKeywordQuery 
	 * @param aKeywordQuery 
	 */
	public LOMQueryOLD(LOM aLOM, String aKeywordQuery, boolean aFuzzyKeywordQuery) {
		considerValueBasedConditions = false;
		isFuzzyKeywordQuery = aFuzzyKeywordQuery;
		itsLOM = aLOM;
		itsKeywordQuery = aKeywordQuery;
	}

	/**
	 * 
	 * 
	 * @param aLOM 
	 * @param aFuzzyValueBasedCondition 
	 */
	public LOMQueryOLD(LOM aLOM, boolean aFuzzyValueBasedCondition) {
		considerValueBasedConditions = true;
		isFuzzyValueBasedCondition = aFuzzyValueBasedCondition;
		itsLOM = aLOM;
		itsKeywordQuery = "";
	}

	/**
	 * return a XQuery only based on restriction diffusion process.
	 * 
	 * @return 
	 */
	public String getDiffusionBasedXQuery() {
		String theQuery = "declare namespace ims='http://www.imsglobal.org/xsd/imsmd_v1p2';\n"
				+ "for $lom in /ims:lom where \n";
		String theConditions = "";
		String restrictionConditions = getRestrictionBasedConditions();
		if (!restrictionConditions.equals("")) {
			if (!theConditions.equals(""))
				theConditions += "\n and ";
			theConditions += "(" + restrictionConditions + ")";
		}
		theQuery += theConditions + "\n return $lom ";
		return theQuery;
	}

	/**
	 * return a list of XQueries only based on keywords process.
	 * 
	 * @return 
	 */
	public List<List<String>> getKeywordBasedXQuery() {
		List<List<String>> theQueries = new ArrayList<List<String>>();
		List<List<String>> theConditionList = getKeywordQueryConditionList();
		for (List<String> theCondition : theConditionList) {
			List<String> theQuery = new ArrayList<String>();
			for (String theString : theCondition)
				theQuery
						.add("declare namespace ims='http://www.imsglobal.org/xsd/imsmd_v1p2';\n"
								+ "for $lom in /ims:lom where "
								+ theString
								+ "\n return $lom ");
			theQueries.add(theQuery);
		}
		return theQueries;
	}

	/**
	 * return a XQuery based on both keyword and restriction difusion process.
	 * 
	 * @return 
	 */
	public String getHybridBasedXQuery() {
		String theQuery = "declare namespace ims='http://www.imsglobal.org/xsd/imsmd_v1p2';\n"
				+ "for $lom in /ims:lom where \n";
		String theConditions = "";
		String keyConditions = getKeywordQueryConditions();
		if (!keyConditions.equals(""))
			theConditions += "(" + keyConditions + ")";
		String restrictionConditions = getRestrictionBasedConditions();
		if (!restrictionConditions.equals("")) {
			if (!theConditions.equals(""))
				theConditions += "\n and ";
			theConditions += "(" + restrictionConditions + ")";
		}
		theQuery += theConditions + "\n return $lom ";
		return theQuery;
	}

	/**
	 * *************************************************************************
	 * return an xquery based on simple keywordquery
	 * 
	 * ************************************************************************.
	 * 
	 * @return 
	 */

	public String getSimpleKeywordQuery() {
		String theQuery = "declare namespace ims='http://www.imsglobal.org/xsd/imsmd_v1p2';\n"
				+ "for $lom in /ims:lom ";
		String theConditions = "";
		String keyConditions = getKeywordQueryConditions();
		if (!keyConditions.equals(""))
			theConditions += "where \n (" + keyConditions + ")";
		theQuery += theConditions + "\n return $lom ";
		return theQuery;
	}

	/**
	 * return the xquery based on three main elements: - a keyword query = each
	 * keyword must be find in at least one attribute of the resulted LOM -
	 * values given to the attributes of the associated LOM = the resulting LOM
	 * should match with these values - the restriction values generated by the
	 * diffusion framework = the resulting LOM should match with the value range
	 * given by the restrictions.
	 * 
	 * @return 
	 */
	public String getXQuery() {
		String theQuery = "declare namespace ims='http://www.imsglobal.org/xsd/imsmd_v1p2';\n"
				+ "for $lom in /ims:lom where \n";
		String theConditions = "";
		String keyConditions = getKeywordQueryConditions();
		if (!keyConditions.equals(""))
			theConditions += "(" + keyConditions + ")";

		if (considerValueBasedConditions) {
			String valueConditions = getValueBasedConditions();
			if (!valueConditions.equals("")) {
				if (!theConditions.equals(""))
					theConditions += "\n and ";
				theConditions += "(" + valueConditions + ")";
			}
		}
		String restrictionConditions = getRestrictionBasedConditions();
		if (!restrictionConditions.equals("")) {
			if (!theConditions.equals(""))
				theConditions += "\n and ";
			theConditions += "(" + restrictionConditions + ")";
		}

		theQuery += theConditions + "\n return $lom ";
		return theQuery;
	}

	/**
	 * return the conditions imposed by the keyword query all term of the query
	 * are looked in any part of the lom.
	 * 
	 * @return 
	 */
	public String getKeywordQueryConditions() {
		if (itsKeywordQuery == null || itsKeywordQuery.equals(""))
			return "";
		String theConditions = "";
		StringTokenizer theTokenizer = new StringTokenizer(itsKeywordQuery
				.replace(",", " "), " ");
		for (; theTokenizer.hasMoreTokens();) {
			String element = (String) theTokenizer.nextToken();
			if (!element.trim().equalsIgnoreCase("")) {
				theConditions += getAnyAttributeContains(element.trim());
				// theConditions += getAnyAttributeContainsStem(element.trim());
				if (theTokenizer.hasMoreTokens())
					theConditions += isFuzzyKeywordQuery ? "\n or " : "\n and ";
			}
		}
		return theConditions;
	}

	/**
	 * return the condition list imposed by the keyword query in the first
	 * elment all term of the query are looked in any part of the lom in the
	 * second only term number -1 are considered in the third only term number -
	 * 1 are considered.
	 * 
	 * @return 
	 */
	public List<List<String>> getKeywordQueryConditionList() {
		List<List<String>> theConditionList = new ArrayList<List<String>>();
		if (itsKeywordQuery == null || itsKeywordQuery.equals(""))
			return theConditionList;
		List<String> theKeywords = new ArrayList<String>();
		StringTokenizer theTokenizer = new StringTokenizer(itsKeywordQuery
				.replace(",", " "), " ");
		for (; theTokenizer.hasMoreTokens();)
			theKeywords.add(theTokenizer.nextToken().trim());

		for (int i = theKeywords.size(); i > 0; i--) {
			List<String> theConditions = new ArrayList<String>();
			List<List<String>> theCombinations = Utils.combinationsOf(
					theKeywords, i);
			for (int j = 0; j < theCombinations.size(); j++) {
				String theKeywordsToFind = "";
				for (String theString : theCombinations.get(j))
					theKeywordsToFind += theString + " ";
				theConditions
						.add(getAnyAttributeContainsStem(theKeywordsToFind));
			}
			theConditionList.add(theConditions);
		}
		return theConditionList;
	}

	/**
	 * return the conditions imposed by the current values associated with the
	 * given lom isFuzzy is a boolean indicating if the conditions must be
	 * strict or may be fuzzied (at most one but not all conditions will have to
	 * be reached.
	 * 
	 * @return 
	 */
	public String getValueBasedConditions() {
		String theConditions = "";
		boolean isFirstLoop = true;
		for (LOMAttribute theAttribute : ITSAttributes) {
			LOMValue theValue = theAttribute.getValueIn(itsLOM);
			if (theValue != null) {
				String theAttributeConditions = "";
				StringTokenizer theTokenizer = new StringTokenizer(theValue
						.getValue(), ",");
				for (; theTokenizer.hasMoreTokens();) {
					String element = (String) theTokenizer.nextToken();
					theAttributeConditions += getAttributeContains(
							theAttribute, element.trim());
					if (theTokenizer.hasMoreTokens())
						theAttributeConditions += isFuzzyValueBasedCondition ? " or"
								: " and ";
				}
				if (!theAttributeConditions.equals("")) {
					if (!isFirstLoop)
						theConditions += isFuzzyValueBasedCondition ? "\n or"
								: "\n and ";
					theConditions += "(" + theAttributeConditions + ")";
					isFirstLoop = false;
				}
			}
		}
		return theConditions;
	}

	/**
	 * returns the conditions imposed by the restricitons generated for this
	 * LOM. Restriction on value may not apply when the required element is
	 * empty
	 * 
	 * @return 
	 */
	public String getRestrictionBasedConditions() {
		String theConditions = "";
		boolean isFirstLoop = true;
		for (LOMAttribute theAttribute : ITSAttributes) {
			String theSetConditions = "";
			LOMRestrictionSet theRestrictionSet = Diffusion.ResDif(itsLOM,
					theAttribute);
			if (theRestrictionSet != null) {
				theRestrictionSet.mergeOperators();
				theSetConditions += getRestrictionsSetCondiditions(theRestrictionSet);
				if (!theSetConditions.equals("")) {
					theSetConditions = "(" + theSetConditions + "\n or "
							+ getEmptyCondition(theAttribute) + ")";
				}
			}
			if (!theSetConditions.equals("")) {
				if (!isFirstLoop)
					theConditions += "\n and ";
				theConditions += "(" + theSetConditions + ")";
				isFirstLoop = false;
			}
		}
		return theConditions;
	}

	/**
	 * returns the conditions imposed by aRestrictionSet passed as parameter
	 * restricion with operator -CONTAINED generates "contains" for each element
	 * of the associated LOMValue with a OR between them -CONTAINS generates
	 * "contains" for each element with a AND between them -EQ generates
	 * "contains" for each element with a AND between them -DIF generates "not
	 * contains" for each element with a AND between them -INF,INF_EQ.
	 * SUP,SUP_EQ generates respectively a <,<=,>,>= for each element of
	 * associated LOMValueInt does not generate anything for LOMVAlueSet
	 * 
	 * @param aSet 
	 * 
	 * @return 
	 */
	public String getRestrictionsSetCondiditions(LOMRestrictionSet aSet) {
		String theConditions = "";
		boolean isFirstLoop = true;
		for (LOMRestrictionValue theRestriction : aSet
				.getLOMRestrictionValues()) {
			String theRestrictionConditions = "";
			LOMValue theValue = theRestriction.getValue();
			RestrictionOperator theOperator = theRestriction.getOperator();
			if (theValue instanceof LOMValueInt) {
				LOMValueInt theValueInt = (LOMValueInt) theValue;
				switch (theOperator) {
				case INF:
					theRestrictionConditions += getAttributeIntCondition(
							theValue.getLOMAttribute(), " lt "
									+ theValueInt.getIntValue());
					break;
				case INF_EQ:
					theRestrictionConditions += getAttributeIntCondition(
							theValue.getLOMAttribute(), " le "
									+ theValueInt.getIntValue());
					break;
				case SUP_EQ:
					theRestrictionConditions += getAttributeIntCondition(
							theValue.getLOMAttribute(), " ge "
									+ theValueInt.getIntValue());
					break;
				case SUP:
					theRestrictionConditions += getAttributeIntCondition(
							theValue.getLOMAttribute(), " gt "
									+ theValueInt.getIntValue());
					break;
				case EQ:
					theRestrictionConditions += getAttributeIntCondition(
							theValue.getLOMAttribute(), " eq "
									+ theValueInt.getIntValue());
					break;
				case DIF:
					theRestrictionConditions += getAttributeIntCondition(
							theValue.getLOMAttribute(), " ne "
									+ theValueInt.getIntValue());
					break;
				}
			} else {
				StringTokenizer theTokenizer = new StringTokenizer(theValue
						.getValue(), ",");
				switch (theOperator) {

				case CONTAINED:
					for (; theTokenizer.hasMoreTokens();) {
						String element = (String) theTokenizer.nextToken();
						theRestrictionConditions += getAttributeContains(
								theValue.getLOMAttribute(), element.trim());
						if (theTokenizer.hasMoreTokens())
							theRestrictionConditions += "\n or ";
					}
					break;

				case CONTAINS:
					for (; theTokenizer.hasMoreTokens();) {
						String element = (String) theTokenizer.nextToken();
						theRestrictionConditions += getAttributeContains(
								theValue.getLOMAttribute(), element.trim());
						if (theTokenizer.hasMoreTokens())
							theRestrictionConditions += "\n and ";
					}
					break;

				case EQ:
					for (; theTokenizer.hasMoreTokens();) {
						String element = (String) theTokenizer.nextToken();
						theRestrictionConditions += getAttributeContains(
								theValue.getLOMAttribute(), element.trim());
						if (theTokenizer.hasMoreTokens())
							theRestrictionConditions += "\n and ";
					}
					break;
				case DIF:
					for (; theTokenizer.hasMoreTokens();) {
						String element = (String) theTokenizer.nextToken();
						theRestrictionConditions += getAttributeNotContains(
								theValue.getLOMAttribute(), element.trim());
						if (theTokenizer.hasMoreTokens())
							theRestrictionConditions += "\n and ";
					}
					break;

				}
			}
			if (!theRestrictionConditions.equals("")) {
				if (!isFirstLoop)
					theConditions += "\n and ";
				theConditions += "(" + theRestrictionConditions + ")";
				isFirstLoop = false;
			}
		}
		return theConditions;

	}

	/**
	 * return contains without case.
	 * 
	 * @param aString 
	 * 
	 * @return 
	 */
	public String getContainsWithoutCase(String aString) {
		return "[fn:contains(fn:lower-case(text()),fn:lower-case('" + aString
				+ "'))]";
	}

	/**
	 * return contains without case with stemming on the keywords "instancia
	 * parametro" -> "*instanci* *parametr*".
	 * 
	 * @param aString 
	 * 
	 * @return 
	 */
	public String getContainsStemWithoutCase(String aString) {
		String theStemmedString = "\"" + SnowballProgram.stem(aString, "es")
				+ "\"";
		// return "[text() &= " + theStemmedString + "]";
		return "[text:fuzzy-match-all(text(),  " + theStemmedString + ")]";

	}

	/**
	 * return not contains without case.
	 * 
	 * @param aString 
	 * 
	 * @return 
	 */
	public String getNotContainsWithoutCase(String aString) {
		return "[fn:not(fn:contains(fn:lower-case(text()),fn:lower-case('"
				+ aString + "')))]";
	}

	/**
	 * return the xquery condition returning true if a lom do not instanciate
	 * anAttribute.
	 * 
	 * @param anAttribute 
	 * 
	 * @return 
	 */
	public String getEmptyCondition(LOMAttribute anAttribute) {
		return "fn:empty($"
				+ anAttribute.getXPath().replaceFirst("ims:lom", "lom")
				+ "/text())";
	}

	/**
	 * return the xquery condition returning true if a lom contains
	 * aStringToLook in anAttribute.
	 * 
	 * @param aCond 
	 * @param anAttribute 
	 * 
	 * @return 
	 */
	public String getAttributeIntCondition(LOMAttribute anAttribute,
			String aCond) {
		return "($" + anAttribute.getXPath().replaceFirst("ims:lom", "lom")
				+ "[ text()" + aCond + "])";
	}

	/**
	 * return the xquery condition returning true if a lom contains
	 * aStringToLook in anAttribute.
	 * 
	 * @param anAttribute 
	 * @param aStringToLookFor 
	 * 
	 * @return 
	 */
	public String getAttributeContains(LOMAttribute anAttribute,
			String aStringToLookFor) {
		return "($" + anAttribute.getXPath().replaceFirst("ims:lom", "lom")
				+ getContainsWithoutCase(aStringToLookFor) + ")";
	}

	/**
	 * return the xquery condition returning true if a lom does not contains
	 * aStringToLook in anAttribute.
	 * 
	 * @param anAttribute 
	 * @param aStringToLookFor 
	 * 
	 * @return 
	 */
	public String getAttributeNotContains(LOMAttribute anAttribute,
			String aStringToLookFor) {
		return "($" + anAttribute.getXPath().replaceFirst("ims:lom", "lom")
				+ getNotContainsWithoutCase(aStringToLookFor) + ")";
	}

	/**
	 * return the xquery condition returning true if a lom contains
	 * aStringToLookFor in any of its attribute.
	 * 
	 * @param aStringToLookFor 
	 * 
	 * @return 
	 */
	public String getAnyAttributeContains(String aStringToLookFor) {
		return "($lom//*" + getContainsWithoutCase(aStringToLookFor) + ")";
	}

	/**
	 * return the xquery condition returning true if a lom contains
	 * aStringToLookFor in any of its attribute using a stem algorithm.
	 * 
	 * @param aStringToLookFor 
	 * 
	 * @return 
	 */
	public String getAnyAttributeContainsStem(String aStringToLookFor) {
		return "($lom//." + getContainsStemWithoutCase(aStringToLookFor) + ")";
	}

	/**
	 * 
	 * 
	 * @param args 
	 */
	public static void main(String[] args) {
		/*
		 * LOM theL1 = new LOM(LOM.class
		 * .getResource("resources/test/TestLOM1.xml")); LOM theL2 = new
		 * LOM(LOM.class .getResource("resources/test/TestLOM2.xml")); LOM theL3 =
		 * new LOM(LOM.class .getResource("resources/test/TestLOM3.xml"));
		 * LOMQuery theQuery = new LOMQuery(theL1, "java", true);
		 * System.out.println(theQuery.getXQuery());
		 */
	}
}
